package bsh;

import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

import bsh.exception.CompilationFailedException;
import bsh.meta.JavaClassWrapper;
import bsh.meta.Scope;
import bsh.meta.TypeWrapper;

/**
 * Compiler para o operador ternario
 * 
 * @author takeshi
 */
public class TernaryExpressionCompiler implements NodeCompiler {

    private final BSHTernaryExpression node;

    private final CompilationUnit compilationUnit;

    /**
     * @param node node
     * @param compilationUnit unit
     */
    public TernaryExpressionCompiler(BSHTernaryExpression node, CompilationUnit compilationUnit) {
        super();
        this.node = node;
        this.compilationUnit = compilationUnit;
    }

    /**
     * {@inheritDoc}
     */
    public void compile(MethodVisitor host, CompilationState state) throws CompilationFailedException {
        int oldStack = state.getStackSize();
        this.compilationUnit.compileNode(this.node.getChild(0), host, state);
        state.assertStackSize(oldStack + 1);
        this.compilationUnit.getCompilerManager().coerceStackTopTo(host, JavaClassWrapper.BOOLEAN_WRAPPER, state);
        state.popStack();
        TypeWrapper wrapper = this.nodeResultType(state.currentScope());
        Label f = new Label(), end = new Label();
        host.visitJumpInsn(Opcodes.IFEQ, f);

        // compile left side
        this.compilationUnit.compileNode(this.node.getChild(1), host, state);
        state.assertStackSize(oldStack + 1);
        this.compilationUnit.getCompilerManager().coerceStackTopTo(host, wrapper, state);
        state.assertStackSize(oldStack + 1);
        state.popStack();
        host.visitJumpInsn(Opcodes.GOTO, end);
        host.visitLabel(f);

        // compile right side
        this.compilationUnit.compileNode(this.node.getChild(2), host, state);
        state.assertStackSize(oldStack + 1);
        this.compilationUnit.getCompilerManager().coerceStackTopTo(host, wrapper, state);
        state.assertStackSize(oldStack + 1);
        state.popStack();
        host.visitLabel(end);
        state.pushStack(wrapper);
        state.assertStackSize(oldStack + 1);
    }

    /**
     * {@inheritDoc}
     */
    public TypeWrapper nodeResultType(Scope scope) throws CompilationFailedException {
        TypeWrapper trueSide = this.compilationUnit.getNodeType(this.node.getChild(1), scope);
        TypeWrapper falseSide = this.compilationUnit.getNodeType(this.node.getChild(2), scope);
        if (trueSide.equals(falseSide)) {
            return trueSide;
        }
        return BinaryExpressionCompiler.promoteTypes(trueSide, falseSide);
    }

}
